/**
 *
 */
package de.cbf.logview.service;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.eclipse.ui.IPartListener2;
import org.eclipse.ui.IPartService;
import org.eclipse.ui.IWindowListener;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.IWorkbenchPartReference;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.part.ViewPart;

import de.cbf.logview.interfaces.ControllerLogView;
import de.cbf.logview.interfaces.ILogView;
import de.cbf.logview.interfaces.ILogViewOpenedListener;


/**
 * @author Nebelritter
 *
 */
public class ServiceLogViewControllerImpl implements IServiceLogViewController {
	private static final Log logger = LogFactory.getLog(ServiceLogViewControllerImpl.class);
	
	protected static ServiceLogViewControllerImpl instance;
	
	protected Map<String,List<ListenerControllerEntry>> id2Entry;	
	protected LogViewPartListener logViewPartListener;
	
	
	private ServiceLogViewControllerImpl() {
		super();
		registerWorkbenchLogViewPartListener();
	}
	
	public static ServiceLogViewControllerImpl getInstance() {
		if(instance == null){
			instance = new ServiceLogViewControllerImpl();			 
			instance.id2Entry = new HashMap<String,List<ListenerControllerEntry>>();
		}
		return instance;
	}
	
	//LATER possibly move to early startup, when workbench is surely there
	protected void registerWorkbenchLogViewPartListener() { 
		if(PlatformUI.isWorkbenchRunning()){
			logViewPartListener = new LogViewPartListener();
			IWorkbench workbench = PlatformUI.getWorkbench();
			addPartListenerToWorkbenchWindows(workbench, logViewPartListener);
			logger.trace("logViewPartListener registered");
		}else{
			logger.warn("logViewPartListener not registered, workbench is not up");
		}
	}

	
	
	@Override
	public void observeLogViewOpen(String id, ILogViewOpenedListener listener) {		
		observeLogViewOpen(id, listener, new ControllerLogView());		
	}
	
	@Override
	public void observeLogViewOpen(String id, ILogViewOpenedListener listener,ControllerLogView controller) {
		controller.setId(id);		
		List<ListenerControllerEntry> entryList = id2Entry.get(id);
		if(entryList == null){
			entryList =	new ArrayList<ListenerControllerEntry>();
		}
		ListenerControllerEntry entry = new ListenerControllerEntry();
		entry.listener = listener;
		entry.controller = controller;
		entryList.add(entry);
		logger.debug("listening for LogView id "+id+" with controller "+controller);
		id2Entry.put(id, entryList);
	}

	@Override
	public void ignoreLogViewOpen(String id, ILogViewOpenedListener listener) {
		if(listener == null){
			return;
		}
		List<ListenerControllerEntry> list = id2Entry.get(id);		
		if(list != null){
			Iterator<ListenerControllerEntry> it = list.iterator();
			while (it.hasNext()) {
				ListenerControllerEntry entry = (ListenerControllerEntry) it.next();
				if(listener.equals(entry.listener)){
					it.remove();
				}			
			}
		}
	}

	protected class ListenerControllerEntry{
		ControllerLogView controller;
		ILogViewOpenedListener listener;
	}
	
	
	protected class LogViewPartListener implements IPartListener2{
		
		protected List<ListenerControllerEntry> findEntrysByPartRef(ILogView logView){
			if (logView instanceof ViewPart) {
				ViewPart part = (ViewPart) logView;
				String secondaryId = part.getViewSite().getSecondaryId();
				return id2Entry.get(secondaryId);
			}			
			return null;
		}
		
		@Override
		public void partOpened(IWorkbenchPartReference partRef) {
			IWorkbenchPart part = partRef.getPart(false);
			if (part instanceof ILogView) {
				ILogView logView = (ILogView) part;
				List<ListenerControllerEntry> list = findEntrysByPartRef(logView);
				if(list != null){
					for (ListenerControllerEntry entry : list) {
						if(entry.listener != null){
							if(entry.controller != null){
								entry.controller.setView(logView);
							}							
							entry.listener.logViewOpened(entry.controller);
						}
					}
				}	
			}					
		}
		
		@Override
		public void partClosed(IWorkbenchPartReference partRef) {
			IWorkbenchPart part = partRef.getPart(false);
			if (part instanceof ILogView) {
				ILogView logView = (ILogView) part;
				List<ListenerControllerEntry> list = findEntrysByPartRef(logView);
				if(list != null){
					for (ListenerControllerEntry entry : list) {
						if(entry.listener != null){
							if(entry.controller != null){
								entry.controller.setView(null);
							}		
							entry.listener.logViewClosed(entry.controller);
						}
					}
				}				
			}			
		}
		
		@Override
		public void partActivated(IWorkbenchPartReference partRef) {}
		@Override
		public void partBroughtToTop(IWorkbenchPartReference partRef) {}
		@Override
		public void partDeactivated(IWorkbenchPartReference partRef) {}		
		@Override
		public void partHidden(IWorkbenchPartReference partRef) {}
		@Override
		public void partVisible(IWorkbenchPartReference partRef) {}
		@Override
		public void partInputChanged(IWorkbenchPartReference partRef) {}
	}
	
	public static void addPartListenerToWorkbenchWindows(IWorkbench workbench,final IPartListener2 listener ) {		
		 //add listener to existing windows
       IWorkbenchWindow[] windows = workbench.getWorkbenchWindows();
       for (int i = 0; i < windows.length; i++) {
			IWorkbenchWindow window = windows[i];
			IPartService partService = window.getPartService();
			partService.addPartListener(listener);
		}
       //no windows open -> add listener if windows are opened
       workbench.addWindowListener(new IWindowListener() {
			@Override
			public void windowOpened(IWorkbenchWindow window) {
				IPartService partService = window.getPartService();
				partService.addPartListener(listener);
			}
			@Override
			public void windowDeactivated(IWorkbenchWindow window) {
			}
			@Override
			public void windowClosed(IWorkbenchWindow window) {
				IPartService partService = window.getPartService();
				partService.removePartListener(listener);
			}
			@Override
			public void windowActivated(IWorkbenchWindow window) {
			}
		});		
	}
}
